Chapter 7 DERIVED TYPES LET'S TALK ABOUT TYPES _________________________________________________________________ We have been talking about types throughout this tutorial, but we've been sort of working our way around them rather than carefully defining the various kinds of types available in Ada. This chapter will be devoted to a full discussion of types. It was not possible to discuss types in full until we covered a bit of material about the various scalar types and how they are used, but with that material behind us, we will give a full treatment to the topic of types. There are four different ways to declare a type. You can probably guess that we will discuss each of the ways in detail in this chapter. They are listed as follows; 1. Predefined types. These are provided for us by the compiler writer as defined in the LRM. We have already used several of these. 2. User defined types. We have already defined a few new types in some of the earlier programs. 3. Derived types. These get their name because they are defined in part based on a previously defined type and derive some of their characteristics from those types. We have not yet encountered the derived type. 4. Subtypes. These are usually a subset of another type upon which they are based. We encountered subtypes in the last chapter. Before we launch into a detailed definition of these four type definition methods, we must devote a little attention to the various type classes. TYPE CLASSES IN ADA _________________________________________________________________ There are five type classes in Ada, a class being a rough grouping of types because of similar characteristics. The five type classes and a brief definition of each is given below. Scalar class - This includes all of the types we have used so far in this tutorial, and are characterized by the fact that each variable of this class can store a single number, either variable or constant. Discrete types are a subset of this Page 7-1 Chapter 7 - Derived Types class and include all integer types, enumerated types, and the CHARACTER type. All real types are included in the scalar class, but not the discrete subclass. The remainder of this chapter will utilize scalar class variables as examples of the various type definition methods. Composite class - This class includes the array types and the record types. Access class - Variables of this type are similar to the pointer type in Pascal or C, and are used to access a variable or group of variables in an indirect manner. Private class - This class of types is used for information hiding techniques when used on a multi-programmer project. Tasking class - This type class is used for concurrent processing. All of these classes will be illustrated in detail later in this tutorial. The concept of typing is one of the most important in Ada, because the proper use of typing will act as a tremendous aid in locating programming errors. Keep in mind that all types are static and cannot be changed during program execution. PREDEFINED TYPES IN ADA _________________________________________________________________ The LRM requires every Ada compiler to provide several predefined types, including INTEGER, NATURAL, POSITIVE, FLOAT, CHARACTER, and STRING. Several other types are optional and include LONG_INTEGER, SHORT_INTEGER, LONG_FLOAT, and SHORT_FLOAT. It will be left to you to study the documentation that came with your compiler and see which of the optional types are included with your compiler package. The predefined types can be used as the basis of the declaration of new types for your specific application and is the topic of this chapter. USER DEFINED TYPES _________________________________________________________________ Examine the program named DERTYPES.ADA for ================ several examples of user defined types. Lines DERTYPES.ADA 10 through 12 each declare an entirely new type. ================ Considering the predefined type INTEGER and these three, we actually have four types to use in the program, and each type has nothing to do with the other three. Variables of these types cannot be intermixed in any way without the proper type conversion explicitly stated by the Page 7-2 Chapter 7 - Derived Types programmer. Moreover, variables declared with one of these types cannot be assigned a value that is outside of its declared range. The structure for declaring a user defined integer class type is given as, type is range ..; As stated earlier, the word range is a reserved word, and its presence indicates to the compiler that you wish to have a type of the integer class. Use of the three new types is not illustrated in this example program, but the diligent student will have no trouble using these new types to declare a few variables, then use them in some mathematical statements. DERIVED TYPES ARE RELATIVELY NEW _________________________________________________________________ Derived types are an entirely new subject, since they are not available in any of the more popular languages that you may have been programming in. Derived types get their name because they are derived from an existing type rather than being an entirely new creation. A derived type has all of the operations available that are available with the parent type but it may have a more limited range than the range of the parent type. The example program has as illustration of this in line 13 where the type TINY_POS is derived from the user defined type POS_INT but with a slightly tighter allowable range. Any operation that is legal to be performed on a variable of type POS_INT is legal for a variable of type TINY_POS. In fact, if we would have declared a subprogram (which we haven't studied yet), prior to line 13, that operated on variables of type POS_INT, then that same subprogram could be used for variables of type TINY_INT. We will have more to say about this when we get to the chapter on subprograms in this tutorial. The key to a derived type is the use of the reserved word new along with the type from which the new type will be derived. The structure for declaring a user defined derived type is given as, type is new ; The derived type will be of the same class as the type from which it is derived, in this case it will be of the integer type class. We continue declaring new types in lines 14 through 16 and because these three are entirely new types, each derived from the parent type INTEGER, they cannot be added together, subtracted, compared or even assigned to each other without a bit of extra trouble. Each of the seven new types in lines 10 through 16 share all of Page 7-3 Chapter 7 - Derived Types the operations that are defined for INTEGER, including the arithmetic, logical, and assignment operations, but the operations can only be performed as long as the types of the objects are consistent. Of course, the explicit type conversion can be done to allow the combination of variables of any of these types. The type TREE_INT, defined in line 16, is a derived type with a more limited range than the parent type so it has all of the characteristics of the parent type except that it cannot be assigned a value outside of its defined range. HOW TO USE SOME OF THE NEW TYPES _________________________________________________________________ As an example of using the new types, consider the type SALAD_INT which is a derived type of the parent type INTEGER. Since three variables, Salad, Lettuce, and Tomatoes, are all of the same type, they can be freely added, compared, assigned to each other, or used in any way legal for integer class variables. They can be used as the iteration index or range limits in a for loop. The three variables have the same range as the parent type INTEGER, namely -32768 to 32767 on most 16 bit machines. Constants of type universal_integer can be added to, compared to, or assigned to these three variables, as well as any of the other variables declared in this program. HOW CAN THESE NEW TYPES HELP IN A PROGRAM? _________________________________________________________________ Everything that was said about type SALAD_INT in the last paragraph is true of type ANIMAL_INT, TREE_INT, or any of the other four types. Suppose somewhere in our program we tried to add the number of Tomatoes to the number of Dogs and assign the result to Trees. If the variable names are meaningful, we would probably not want to do such an operation in any practical program. The Ada compiler would give us a compile time error, so we would detect the error before we tried to run the program with such a silly statement. Careful assignment of types can be used to protect us from the silly little errors that we are all so prone to make. It would be much more efficient to let the compiler find these silly little errors and free us up to find the analysis errors we also make. HOW DO YOU DO A TYPE TRANSFORMATION? _________________________________________________________________ Even though you set things up very carefully, you may need to perform some operations on the data where you actually do need to add the number of Animals to the total of Oak plus Coconut. Line Page 7-4 Chapter 7 - Derived Types 33 illustrates how to do this. Enclosing the variable in parentheses and adding the desired type to the front of the grouping will change the type from the variable's actual type to the type in front of the parentheses, but only for that one place in the program. If you want to use the type transformation again, you add the type in front of the variable again. In line 34, Trees and Salad are both transformed to type INTEGER before being summed and assigned to Count, a variable of type INTEGER. Line 36 illustrates the addition of many variables by first transforming each to type SALAD_INT then performing the addition. In line 43, the same variables are added together, but in this case, all variables are transformed into type ANIMAL_INT prior to summing, then the sum is transformed to type SALAD_INT. The two methods should result in the same answer, and you can verify that they do when you compile and run the program. WHAT IS A SUBTYPE? _________________________________________________________________ A subtype is a new type based on a parent type but usually with a more restricted range. It inherits all of the characteristics of the parent type and in addition, it can be freely intermixed with the parent type in calculations and assignment statements. The reason for using a subtype is usually to declare a variable of the parent type but with a more limited range to take advantage of the range checking capability of Ada. WE CAN ALSO HAVE SUBTYPES OF DERIVED TYPES _________________________________________________________________ The program named DERSUBS.ADA gives an example =============== of the definition and use of a subtype of a DERSUBS.ADA derived type, and a derived type of a subtype. =============== In line 10 we declare a derived type and in line 13 we declare a subtype of the new derived type. The subtype of the derived type has the same characteristics as the derived type except that it has a more restricted range in this case. Variables of type NEW_SUBTYPE are compatible with variables of their parent type NEW_INT. This is illustrated in line 29. In this case, NEW_SUBTYPE is as different from the type INTEGER, as SALAD_INT was in the last program. WE CAN HAVE A DERIVED TYPE OF A SUBTYPE _________________________________________________________________ Line 15 illustrates the declaration of a derived type based on using a subtype for the parent type. Note that the new derived Page 7-5 Chapter 7 - Derived Types type has all of the characteristics of its parent type except for the more restricted range, but once again, it is an entirely new type as far as type checking is concerned. A SUBTYPE CAN BE SIMPLY A SYNONYM _________________________________________________________________ Line 13 illustrates a subtype which covers the entire range of its parent type. Since variables of this subtype can be freely intermixed with variables of its parent type, the subtype name is simply a synonym for the parent type name. With the discussion of the last program fresh in your mind, you should breeze through the remainder of this program. Be sure to compile and execute it. USING OTHER PREDEFINED TYPES FOR THE PARENT _________________________________________________________________ Examine the program named MOREDERS.ADA for ================ examples of derived types and subtypes based on MOREDERS.ADA some of the other predefined types in Ada. We ================ begin by declaring two user defined types in lines 8 and 9 which are of the floating point class of types because of the reserved word digits appearing in the definition. In line 10 we declare DER_FLOAT which has all of the characteristics of the predefined type FLOAT, except that the compiler will consider it to be an entirely different type and will not allow mixing of these two types. Of course, type conversion can be used if necessary. The derived type LIM_FLOAT is declared with all the characteristics of FLOAT except that it has a limited range to allow for compiler checks. Line 12 contains the definition of a subtype based on DER_FLOAT with a more limited range. Variables of the type SUB_FLOAT can be freely intermixed with variables of type DER_FLOAT, but not with those declared with the types FLOAT, NEW_FLOAT1, NEW_FLOAT2, or LIM_FLOAT. Lines 15 through 19 illustrate the same principles applied to fixed point types and should be self explanatory. The only difference is the use of the reserved word delta in the fixed point definitions. Lines 22 through 29 illustrate the declaration of derived types and subtypes of the CHARACTER type and an enumerated type. These statements will be left to the students study since they are so similar to the example using FLOAT as the parent type. Page 7-6 Chapter 7 - Derived Types A few variables are declared and some are initialized in lines 32 through 35, and a nonsense calculation is given in line 39 to illustrate the type transformations that can be done with derived types. Be sure to compile and execute this program even though it has no output. A WORD OF SUMMARY ABOUT TYPES _________________________________________________________________ We have seen that in addition to the predefined types, we can declare additional types for use in our programs. We can then use any of the predefined or user defined types as the parent type for either subtypes or derived types. The new subtype or derived type can be used as a parent type for additional subtypes or derived types and we find that we have a tremendous amount of flexibility in defining the data to solve any particular problem. PROGRAMMING EXERCISE _________________________________________________________________ 1. Modify the program named DERTYPES.ADA to include a new instantiation of the package Text_IO.Integer_IO to output the variable named Salad in line 40 and 47 without the type conversion. Page 7-7